/* eslint-disable max-statements */ import type { MDXComponents } from 'mdx/types'; import type { GetStaticPaths, GetStaticProps } from 'next'; import dynamic from 'next/dynamic'; import Head from 'next/head'; import NextImage, { type ImageProps as NextImageProps } from 'next/image'; import { useRouter } from 'next/router'; import Script from 'next/script'; import type { ComponentType, HTMLAttributes, ReactNode } from 'react'; import { useIntl } from 'react-intl'; import { Code, getLayout, Link, SharingWidget, Spinner, Heading, List, ListItem, Figure, Grid, ProjectOverview, type ProjectMeta, type Repository, Page, PageHeader, PageSidebar, TocWidget, PageBody, } from '../../components'; import styles from '../../styles/pages/project.module.scss'; import type { NextPageWithLayout, ProjectPreview, Repos } from '../../types'; import { CONFIG } from '../../utils/config'; import { ROUTES } from '../../utils/constants'; import { getSchemaJson, getSinglePageSchema, getWebPageSchema, } from '../../utils/helpers'; import { getProjectData, getProjectFilenames, loadTranslation, type Messages, } from '../../utils/helpers/server'; import { useBreadcrumb, useGithubApi, useHeadingsTree, } from '../../utils/hooks'; const BorderedImage = (props: NextImageProps) => (
); const H1 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H2 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H3 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H4 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H5 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H6 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const OrderedList = ({ children, ...props }: HTMLAttributes) => ( {children} ); const UnorderedList = ({ children, ...props }: HTMLAttributes) => ( {children} ); const Gallery = ({ children }: { children: ReactNode[] }) => ( { return { id: `${index}`, item: child }; })} sizeMin="250px" /> ); const components: MDXComponents = { Code, Gallery, h1: H1, h2: H2, h3: H3, h4: H4, h5: H5, h6: H6, Image: BorderedImage, li: ({ ref, ...props }) => , Link, ol: OrderedList, ul: UnorderedList, }; type ProjectPageProps = { project: ProjectPreview; translation: Messages; }; /** * Project page. */ const ProjectPage: NextPageWithLayout = ({ project }) => { const { id, intro, meta, title } = project; const { cover, dates, license, repos, seo, technologies } = meta; const intl = useIntl(); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ title, url: `${ROUTES.PROJECTS}/${id}`, }); const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const ProjectContent: ComponentType = dynamic( async () => import(`../../content/projects/${id}.mdx`), { loading: () => , } ); const { asPath } = useRouter(); const page = { title: `${seo.title} - ${CONFIG.name}`, url: `${CONFIG.url}${asPath}`, }; /** * Retrieve the project repositories. * * @param {Repos} repositories - A repositories object. * @returns {Repository[]} - An array of repositories. */ const getRepos = (repositories: Repos): Repository[] => { const definedRepos: Repository[] = []; if (repositories.github) definedRepos.push({ id: 'Github', label: intl.formatMessage({ defaultMessage: 'Github profile', description: 'ProjectsPage: Github profile link', id: 'Nx8Jo5', }), url: repositories.github, }); if (repositories.gitlab) definedRepos.push({ id: 'Gitlab', label: intl.formatMessage({ defaultMessage: 'Gitlab profile', description: 'ProjectsPage: Gitlab profile link', id: 'sECHDg', }), url: repositories.gitlab, }); return definedRepos; }; const loadingRepoPopularity = intl.formatMessage({ defaultMessage: 'Loading the repository popularity...', description: 'ProjectsPage: loading repository popularity', id: 'RwI3B9', }); const { isError, isLoading, data } = useGithubApi( /* * Repo should be defined for each project so for now it is safe for my * use-case. However, I should refactored it to handle cases where it is * not defined. The logic should be extracted in another component I think. * * TODO: fix this hardly readable argument */ meta.repos ? meta.repos.github ?? '' : '' ); if (isError) return 'Error'; if (isLoading || !data) return ; const overviewMeta: Partial = { creationDate: data.created_at, lastUpdateDate: data.updated_at, license, popularity: repos?.github ? { count: data.stargazers_count, url: `https://github.com/${repos.github}/stargazers`, } : undefined, repositories: repos ? getRepos(repos) : undefined, technologies, }; const webpageSchema = getWebPageSchema({ description: seo.description, locale: CONFIG.locales.defaultLocale, slug: asPath, title: seo.title, updateDate: dates.update, }); const articleSchema = getSinglePageSchema({ cover: `/projects/${id}.jpg`, dates, description: intro, id: 'project', kind: 'page', locale: CONFIG.locales.defaultLocale, slug: asPath, title, }); const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]); const sharingWidgetTitle = intl.formatMessage({ defaultMessage: 'Share', id: 'HKKkQk', description: 'SharingWidget: widget title', }); const tocTitle = intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'PageLayout: table of contents title', id: 'eys2uX', }); return ( {page.title} {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}